package esrc.c9cf.schedulers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.realtime.AbsoluteTime;
import javax.realtime.NativeHelper;
import javax.realtime.PeriodicParameters;
import javax.realtime.PriorityParameters;
import javax.realtime.RealtimeThread;
import javax.realtime.ReleaseParameters;
import javax.realtime.Schedulable;
import javax.realtime.SchedulingParameters;
import javax.realtime.RealtimeThread.RealtimeThreadState;

import esrc.c9cf.Scheduler;

public class PartitionedPriorityScheduler extends Scheduler {
	private static final int MAX_PRIORITY = NativeHelper.getMaxRTPriority();
	private static final int MIN_PRIORITY = NativeHelper.getMinRTPriority();
	private static final int NORM_PRIORITY = MIN_PRIORITY + (MAX_PRIORITY-MIN_PRIORITY)/2;
	
	private static PartitionedPriorityScheduler instance = new PartitionedPriorityScheduler();
	protected PartitionedPriorityScheduler() {
		super();
	}
	public static PartitionedPriorityScheduler instance() {
		return instance;
	}
	
	public static int getMaxPriority() {
		return MAX_PRIORITY;
	}
	public static int getMinPriority() {
		return MIN_PRIORITY;
	}
	public static int getNormPriority(){
		return NORM_PRIORITY;
	}

	@Override
	public void fireSchedulable(Schedulable schedulable) {
		throw new UnsupportedOperationException();
	}

	@Override
	public ReleaseParameters getDefaultReleaseParameters() {
		//TODO: will return adequate one when AperiodicParameters is available 
		return null;
	}

	@Override
	public SchedulingParameters getDefaultSchedulingParameters() {
		return new PriorityParameters(NORM_PRIORITY, this);
	}

	@Override
	public String getPolicyName() {
		return "Partitioned Fixed Priority";
	}

	@Override
	public boolean isFeasible() {
		// TODO Auto-generated method stub
		float feasibility = 0;
		for(Schedulable schedulable : feasibilitySet)
		{
			schedulable.getReleaseParameters().getCost();
		}
		return false;
	}
	
	@Override
	public void reschedule(AbsoluteTime theTime, Schedulable schedulable) {
		ArrayList<RealtimeThread> readyQ = new ArrayList<RealtimeThread>();
		//RealtimeThread running = null;
		synchronized(feasibilitySet)
		{
			for(Schedulable schedulables_in_the_set : feasibilitySet)
			{
				if(schedulable instanceof RealtimeThread)
				{
					RealtimeThread schedulable_rtt = (RealtimeThread)schedulable;
					if(schedulables_in_the_set instanceof RealtimeThread)
					{
						SchedulingParameters scheduling_rtt = schedulable_rtt.getSchedulingParameters();
						if(!(scheduling_rtt instanceof PriorityParameters)) throw new RuntimeException();
						RealtimeThread rtt_set = (RealtimeThread) schedulables_in_the_set;
						int rtt_cpu = schedulable_rtt.getCurrentCPU();
						int rtt_set_cpu = rtt_set.getCurrentCPU();
						if(rtt_cpu == rtt_set_cpu)
						{
							if(rtt_set.getReleaseParameters() instanceof PeriodicParameters)
							{
								//System.out.println("time "+theTime+ ", tid:"+rtt_set.getTID()+", state:"+rtt_set.getRealtimeThreadState().toString()+", nextReleaseTime: "+ rtt_set.getNextReleaseTime());
								if((rtt_set.getRealtimeThreadState() == RealtimeThreadState.RTT_WAITING) && (theTime.compareTo(rtt_set.getNextReleaseTime()) >= 0))
								{
									rtt_set.setRealtimeThreadState(RealtimeThreadState.RTT_READY);
									readyQ.add(rtt_set);
								}
							}
						}
					}
				}
			}
			
			// sort by priority&nextReleaseTime, ascending
			class RTTComparator implements Comparator<RealtimeThread> {

				@Override
				public int compare(RealtimeThread arg0, RealtimeThread arg1) {
					int releaseTimeCompareVal = arg1.getNextReleaseTime().compareTo(arg0.getNextReleaseTime());
					int priorityCompareVal = 0;
					SchedulingParameters param0 = arg0.getSchedulingParameters();
					SchedulingParameters param1 = arg1.getSchedulingParameters();
					if(param0 instanceof PriorityParameters && param1 instanceof PriorityParameters)
					{
						int prior0 = ((PriorityParameters)param0).getPriority();
						int prior1 = ((PriorityParameters)param1).getPriority();
						if(prior1 > prior0) priorityCompareVal = 1;
						else if(prior1 == prior0) priorityCompareVal = 0;
						else priorityCompareVal = -1;
					}
					
					if(priorityCompareVal == 1) return 1;
					else if(priorityCompareVal == 0) return releaseTimeCompareVal;
					else return -1;
				}
			}
			Collections.sort(readyQ, new RTTComparator());
			for(RealtimeThread rt : readyQ)
			{
				Dispatcher.Dispatch(rt, rt.getCurrentCPU());
			}
		}
	}
	
	/*
	@Override
	synchronized public boolean reschedulePeriodic(Schedulable schedulable)
	{
		Schedulable eligible = null;
		if(schedulable instanceof RealtimeThread)
		{
			RealtimeThread rtt_schedulable = (RealtimeThread)schedulable;
			for(Schedulable tmp_schedulable : feasibilitySet)
			{
				if(tmp_schedulable instanceof RealtimeThread)
				{
					RealtimeThread rtt = (RealtimeThread) tmp_schedulable;
					if(rtt.getCurrentCPU() == rtt_schedulable.getCurrentCPU())
					{
						if(rtt.getReleaseParameters() instanceof PeriodicParameters)
						{
					if((rtt.getRealtimeThreadState() == RealtimeThreadState.RTT_NEW || rtt.getRealtimeThreadState() == RealtimeThreadState.RTT_WAITING) && theTime.compareTo(rtt.getNextStartTime()) >= 0)
					{
						//cpu buckets
						if(eligibles[rtt_cpu] == null) eligibles[rtt_cpu] = rtt;
						else
						{
							// eligible - rtt priority compare
							SchedulingParameters scheduling_eligible = eligibles[rtt_cpu].getSchedulingParameters();
							SchedulingParameters scheduling_rtt = rtt.getSchedulingParameters();
							if(scheduling_eligible instanceof PriorityParameters && scheduling_rtt instanceof PriorityParameters)
							{
								if(((PriorityParameters)scheduling_eligible).getPriority() < ((PriorityParameters)scheduling_rtt).getPriority()) eligibles[rtt_cpu] = rtt;
							}
							else throw new RuntimeException("Why do we have a task with non-priority based scheduling parameters in FP scheduler's feasibility set?");
						}
					}
				}}
				}
			}
		}
		
		for(int i = 0; i < cpuz; i++)
		{
			if(eligibles[i] != null)
			{
				if(eligibles[i] instanceof RealtimeThread)
				{
					((RealtimeThread)eligibles[i]).setCurrentCPU(i);
					Dispatcher.Dispatch((RealtimeThread)eligibles[i], i);
				}
			}
		}
		return true;
	}*/

}
